﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;

namespace TheGame
{
    //MudokonAI definisce l'automa per la gestione del comportamento di un Mudokon
    //[pp.43]
    public class MudokonAI
    {
        #region VARS
        public Game1 game;
        PRECONDITIONm precond = null;
        bool FINISHEDExecution = false;
        bool FINISHEDTransition = true;
        public Mudokon Mudokon;
        public STATEm CURRENT_STATE;
        public STATEm PREV_STATE;

        public STATEm CLEANUP;
        public STATEm CLEANDOWN;
        public STATEm WAIT;
        public STATEm WALK;
        public STATEm JUMP;
        public STATEm SNEAK;
        public STATEm RUN;
        public STATEm RUNSTART;
        public STATEm RUNSTOP;
        public STATEm RUNJUMP;
        public STATEm RUNJUMPESCAPE;
        public STATEm WAITfOLLOW;
        public STATEm WAITCOMMAND;
        public STATEm JUMPCOMMAND;
        public STATEm RUNJUMPCOMMAND1;
        public STATEm RUNJUMPCOMMAND2;
        public STATEm RUNJUMPCOMMAND3;
        public STATEm RUNJUMPCOMMAND4;
        public STATEm GOTOABE;
        public STATEm DIED;
        public STATEm SHOOTED;
        public STATEm GODOWN;

        public PRECONDITIONSm PRECONDITIONS;
        public ACTIONSm ACTIONS; 
          
        List<string> colTypes = new List<string>();
        #endregion

        public MudokonAI() { }
        public MudokonAI(Mudokon Mudokon, Game1 game,string state)
        {
            this.game = game;
            this.Mudokon = Mudokon;


            PRECONDITIONS = new PRECONDITIONSm(this);
            ACTIONS = new ACTIONSm(this);

            DIED = new STATEm("DIED", this);
            CLEANUP = new STATEm("CLEANUP", this);
            CLEANDOWN = new STATEm("CLEANDOWN", this);
            WAIT = new STATEm("WAIT", this);
            WALK = new STATEm("WALK", this);
            JUMP = new STATEm("JUMP", this);
            SNEAK = new STATEm("SNEAK", this);
            WAITfOLLOW = new STATEm("WAITfOLLOW", this);
            WAITCOMMAND = new STATEm("WAITCOMMAND", this);
            JUMPCOMMAND = new STATEm("JUMPCOMMAND", this);
            RUNJUMPCOMMAND1 = new STATEm("RUNJUMPCOMMAND1", this);
            RUNJUMPCOMMAND2 = new STATEm("RUNJUMPCOMMAND2", this);
            RUNJUMPCOMMAND3 = new STATEm("RUNJUMPCOMMAND3", this);
            RUNJUMPCOMMAND4 = new STATEm("RUNJUMPCOMMAND4", this);
            GOTOABE = new STATEm("GOTOABE", this);
            RUN = new STATEm("RUN", this);
            RUNSTART = new STATEm("RUNSTART", this);
            RUNSTOP = new STATEm("RUNSTOP", this);
            RUNJUMP = new STATEm("RUNJUMP", this);
            RUNJUMPESCAPE = new STATEm("RUNJUMPESCAPE", this);
            GODOWN = new STATEm("GODOWN", this);
            SHOOTED = new STATEm("SHOOTED", this);

            CLEANUP.LinkTO(RUNSTART).addPRECONDITION(PRECONDITIONS.PORTALLEFT,ACTIONS.CLEANUPSTOPLEFT);
            CLEANUP.LinkTO(RUNSTART).addPRECONDITION(PRECONDITIONS.PORTALRIGHT, ACTIONS.CLEANUPSTOPRIGHT);
            CLEANUP.LinkTO(GODOWN).addPRECONDITION(PRECONDITIONS.GODOWN);
            CLEANUP.LinkTO(WAITfOLLOW).addPRECONDITION(PRECONDITIONS.ABeHELLOLEFT, ACTIONS.AND(ACTIONS.CLEANUPSTOPLEFT,ACTIONS.HELLO));
            CLEANUP.LinkTO(WAITfOLLOW).addPRECONDITION(PRECONDITIONS.ABeHELLORIGHT, ACTIONS.AND(ACTIONS.CLEANUPSTOPRIGHT, ACTIONS.HELLO));

            CLEANDOWN.LinkTO(RUNSTART).addPRECONDITION(PRECONDITIONS.PORTALOPENAHEAD, ACTIONS.STANDUP);
            CLEANDOWN.LinkTO(RUNSTART).addPRECONDITION(PRECONDITIONS.PORTALOPENBEHIND,ACTIONS.AND(ACTIONS.TURNDOWN,ACTIONS.STANDUP));
            CLEANDOWN.LinkTO(GODOWN).addPRECONDITION(PRECONDITIONS.GODOWN);
            CLEANDOWN.LinkTO(WAITfOLLOW).addPRECONDITION(PRECONDITIONS.ABeHELLOA, ACTIONS.AND(ACTIONS.STANDUP, ACTIONS.HELLO));
            //CLEANDOWN.LinkTO(WAITfOLLOW).addPRECONDITION(PRECONDITIONS.ABeHELLOB, ACTIONS.AND(ACTIONS.TURNDOWN, ACTIONS.AND(ACTIONS.STANDUP, ACTIONS.HELLO)));

            WAITfOLLOW.LinkTO(RUNSTART).addPRECONDITION(PRECONDITIONS.PORTALOPENAHEAD);
            WAITfOLLOW.LinkTO(RUNSTART).addPRECONDITION(PRECONDITIONS.PORTALOPENBEHIND, ACTIONS.TURN);
            WAITfOLLOW.LinkTO(GODOWN).addPRECONDITION(PRECONDITIONS.GODOWN);
            WAITfOLLOW.LinkTO(WAIT).addPRECONDITION(PRECONDITIONS.ABeFOLLOW, ACTIONS.OK);
            WAITfOLLOW.LinkTO(CLEANDOWN).addPRECONDITION(PRECONDITIONS.ABEWAITME, ACTIONS.AND(ACTIONS.OK,ACTIONS.CROUCH));

            GOTOABE.LinkTO(WAITCOMMAND).addPRECONDITION(PRECONDITIONS.ABESAMEPOSITION,ACTIONS.STOP);

            WAIT.LinkTO(RUNJUMPCOMMAND1).addPRECONDITION(PRECONDITIONS.ABECOMMANDRUNJUMP);
            WAIT.LinkTO(JUMPCOMMAND).addPRECONDITION(PRECONDITIONS.ABECOMMANDJUMP,ACTIONS.JUMP1);
            WAIT.LinkTO(WAIT).addPRECONDITION(PRECONDITIONS.AND(PRECONDITIONS.ABECOMMANDJUMP, PRECONDITIONS.BLOCK), ACTIONS.AND(ACTIONS.KO, ACTIONS.GETUP));

            WAIT.LinkTO(GOTOABE).addPRECONDITION(PRECONDITIONS.ABECALLA, ACTIONS.PASS1);
            WAIT.LinkTO(GOTOABE).addPRECONDITION(PRECONDITIONS.ABECALLB, ACTIONS.AND(ACTIONS.TURN, ACTIONS.PASS1));

            WAIT.LinkTO(RUNSTART).addPRECONDITION(PRECONDITIONS.PORTALOPENAHEAD);
            WAIT.LinkTO(RUNSTART).addPRECONDITION(PRECONDITIONS.PORTALOPENBEHIND,ACTIONS.TURN);
            WAIT.LinkTO(GODOWN).addPRECONDITION(PRECONDITIONS.GODOWN);
            WAIT.LinkTO(WAIT).addPRECONDITION(PRECONDITIONS.BLOCK,ACTIONS.TURN);
            WAIT.LinkTO(WAIT).addPRECONDITION(PRECONDITIONS.ABEBEHIND, ACTIONS.TURN);
            WAIT.LinkTO(CLEANDOWN).addPRECONDITION(PRECONDITIONS.ABEWAITME, ACTIONS.AND(ACTIONS.OK, ACTIONS.CROUCH));
            WAIT.LinkTO(SNEAK).addPRECONDITION(PRECONDITIONS.AND(PRECONDITIONS.ABESNEAK, PRECONDITIONS.ABEBEHIND), ACTIONS.AND(ACTIONS.TURN, ACTIONS.SNEAK1));
            WAIT.LinkTO(SNEAK).addPRECONDITION(PRECONDITIONS.AND(PRECONDITIONS.ABESNEAK, PRECONDITIONS.NOTABEOPPOSITE), ACTIONS.SNEAK1);
            WAIT.LinkTO(WALK).addPRECONDITION(PRECONDITIONS.AND(PRECONDITIONS.ABEwALK, PRECONDITIONS.AND(PRECONDITIONS.ABEBEHIND,PRECONDITIONS.ABENOTNEAR)), ACTIONS.AND(ACTIONS.TURN, ACTIONS.PASS1));
            WAIT.LinkTO(WALK).addPRECONDITION(PRECONDITIONS.AND(PRECONDITIONS.ABEwALK, PRECONDITIONS.AND(PRECONDITIONS.NOTABEOPPOSITE,PRECONDITIONS.ABENOTNEAR)), ACTIONS.PASS1);
           
            //WAIT.LinkTO(WAIT).addPRECONDITION(PRECONDITIONS.AND(PRECONDITIONS.ABEOPPOSITE, PRECONDITIONS.ABERUNSTART)
            WAIT.LinkTO(RUNSTART).addPRECONDITION(PRECONDITIONS.AND(PRECONDITIONS.ABERUNSTART, PRECONDITIONS.AND(PRECONDITIONS.ABEBEHIND, PRECONDITIONS.ABENOTNEAR)), ACTIONS.TURN);
            WAIT.LinkTO(RUNSTART).addPRECONDITION(PRECONDITIONS.AND(PRECONDITIONS.ABERUNSTART, PRECONDITIONS.AND(PRECONDITIONS.NOTABEOPPOSITE, PRECONDITIONS.ABENOTNEAR)));
            WAIT.LinkTO(RUNSTART).addPRECONDITION(PRECONDITIONS.AND(PRECONDITIONS.ABERUN, PRECONDITIONS.AND(PRECONDITIONS.NOTABEOPPOSITE, PRECONDITIONS.ABENOTNEAR)));
            WAIT.LinkTO(WALK).addPRECONDITION(PRECONDITIONS.ABENOTNEAR, ACTIONS.PASS1);

            WAIT.LinkTO(WAITCOMMAND).addPRECONDITION(PRECONDITIONS.HOISTTURN, ACTIONS.STOP);
            WAIT.LinkTO(WAITCOMMAND).addPRECONDITION(PRECONDITIONS.HOIST, ACTIONS.STOP);
            WAIT.LinkTO(WAITCOMMAND).addPRECONDITION(PRECONDITIONS.AND(PRECONDITIONS.ABEUP, PRECONDITIONS.APPIGLIOTURN), ACTIONS.STOP);
            WAIT.LinkTO(WAITCOMMAND).addPRECONDITION(PRECONDITIONS.AND(PRECONDITIONS.ABEUP, PRECONDITIONS.APPIGLIO), ACTIONS.STOP);
            
            //WALK.LinkTO(CLEANDOWN).addPRECONDITION(PRECONDITIONS.ENSsCREEN);
            WALK.LinkTO(WAITCOMMAND).addPRECONDITION(PRECONDITIONS.HOISTTURN, ACTIONS.STOP);
            WALK.LinkTO(WAITCOMMAND).addPRECONDITION(PRECONDITIONS.HOIST, ACTIONS.STOP);
            WALK.LinkTO(WAITCOMMAND).addPRECONDITION(PRECONDITIONS.AND(PRECONDITIONS.ABEUP,PRECONDITIONS.APPIGLIOTURN), ACTIONS.STOP);
            WALK.LinkTO(WAITCOMMAND).addPRECONDITION(PRECONDITIONS.AND(PRECONDITIONS.ABEUP, PRECONDITIONS.APPIGLIO), ACTIONS.STOP);

            WAITCOMMAND.LinkTO(RUNJUMPCOMMAND1).addPRECONDITION(PRECONDITIONS.ABECOMMANDRUNJUMP);
            WAITCOMMAND.LinkTO(JUMPCOMMAND).addPRECONDITION(PRECONDITIONS.ABECOMMANDJUMP, ACTIONS.JUMP1);
            WAITCOMMAND.LinkTO(WAIT).addPRECONDITION(PRECONDITIONS.AND(PRECONDITIONS.HOISTTURN, PRECONDITIONS.ABEDOWN), ACTIONS.AND(ACTIONS.AND(ACTIONS.TURN, ACTIONS.HOISTDOWN), ACTIONS.JUMPDOWN));
            WAITCOMMAND.LinkTO(WAIT).addPRECONDITION(PRECONDITIONS.AND(PRECONDITIONS.HOIST, PRECONDITIONS.ABEDOWN), ACTIONS.AND(ACTIONS.HOISTDOWN, ACTIONS.JUMPDOWN));           
            WAITCOMMAND.LinkTO(WAIT).addPRECONDITION(PRECONDITIONS.AND(PRECONDITIONS.APPIGLIO, PRECONDITIONS.ABEUP), ACTIONS.AND(ACTIONS.JUMPUP,ACTIONS.HOISTUP));
            WAITCOMMAND.LinkTO(WAIT).addPRECONDITION(PRECONDITIONS.AND(PRECONDITIONS.APPIGLIOTURN, PRECONDITIONS.ABEUP), ACTIONS.AND(ACTIONS.AND(ACTIONS.TURN,ACTIONS.JUMPUP), ACTIONS.HOISTUP));
            WAITCOMMAND.LinkTO(WAIT).addPRECONDITION(PRECONDITIONS.AND(PRECONDITIONS.ABESAMEFLOOR,PRECONDITIONS.AND(PRECONDITIONS.HOIST,PRECONDITIONS.ABEAHEAD)));
            WAITCOMMAND.LinkTO(WAIT).addPRECONDITION(PRECONDITIONS.AND(PRECONDITIONS.ABESAMEFLOOR, PRECONDITIONS.AND(PRECONDITIONS.HOISTTURN, PRECONDITIONS.ABEBEHIND)), ACTIONS.TURN);
            WAITCOMMAND.LinkTO(WAITCOMMAND).addPRECONDITION(PRECONDITIONS.AND(PRECONDITIONS.ABESAMEFLOOR, PRECONDITIONS.AND(PRECONDITIONS.HOIST, PRECONDITIONS.ABEBEHIND)), ACTIONS.TURN);
            WAITCOMMAND.LinkTO(WAIT).addPRECONDITION(PRECONDITIONS.NOTHOIST);
            WAITCOMMAND.LinkTO(WAIT).addPRECONDITION(PRECONDITIONS.AND(PRECONDITIONS.ABECOMMANDJUMP,PRECONDITIONS.BLOCK), ACTIONS.AND(ACTIONS.KO, ACTIONS.GETUP));

            JUMPCOMMAND.LinkTO(WAITCOMMAND).addPRECONDITION(PRECONDITIONS.BLOCK, ACTIONS.AND(ACTIONS.KO,ACTIONS.GETUP));
            JUMPCOMMAND.LinkTO(GODOWN).addPRECONDITION(PRECONDITIONS.AND(PRECONDITIONS.ENDSTATE,PRECONDITIONS.GODOWN));
            JUMPCOMMAND.LinkTO(WAITCOMMAND).addPRECONDITION(PRECONDITIONS.ENDSTATE, ACTIONS.JUMP4);

            RUNJUMPCOMMAND1.LinkTO(WAITCOMMAND).addPRECONDITION(PRECONDITIONS.BLOCK, ACTIONS.STOP);
            //RUNJUMPCOMMAND1.LinkTO(GODOWN).addPRECONDITION(PRECONDITIONS.GODOWN);
            RUNJUMPCOMMAND1.LinkTO(RUNJUMPCOMMAND2).addPRECONDITION(PRECONDITIONS.ENDSTATE);

            RUNJUMPCOMMAND2.LinkTO(WAITCOMMAND).addPRECONDITION(PRECONDITIONS.BLOCK, ACTIONS.AND(ACTIONS.KO, ACTIONS.GETUP));
            //RUNJUMPCOMMAND2.LinkTO(GODOWN).addPRECONDITION(PRECONDITIONS.GODOWN);
            RUNJUMPCOMMAND2.LinkTO(RUNJUMPCOMMAND3).addPRECONDITION(PRECONDITIONS.ENDSTATE,ACTIONS.RUNJUMP1);

            RUNJUMPCOMMAND3.LinkTO(WAITCOMMAND).addPRECONDITION(PRECONDITIONS.BLOCK, ACTIONS.AND(ACTIONS.KO, ACTIONS.GETUP));
            //RUNJUMPCOMMAND3.LinkTO(GODOWN).addPRECONDITION(PRECONDITIONS.GODOWN);
            RUNJUMPCOMMAND3.LinkTO(RUNJUMPCOMMAND4).addPRECONDITION(PRECONDITIONS.ENDSTATE);

            RUNJUMPCOMMAND4.LinkTO(WAITCOMMAND).addPRECONDITION(PRECONDITIONS.BLOCK, ACTIONS.AND(ACTIONS.KO, ACTIONS.GETUP));
            RUNJUMPCOMMAND4.LinkTO(GODOWN).addPRECONDITION(PRECONDITIONS.GODOWN);
            RUNJUMPCOMMAND4.LinkTO(WAITCOMMAND).addPRECONDITION(PRECONDITIONS.ENDSTATE);

            GOTOABE.LinkTO(WAIT).addPRECONDITION(PRECONDITIONS.ABESAMEPOSITION, ACTIONS.STOP);        

            WAIT.LinkTO(GOTOABE).addPRECONDITION(PRECONDITIONS.ABECALLA, ACTIONS.PASS1);
            WAIT.LinkTO(GOTOABE).addPRECONDITION(PRECONDITIONS.ABECALLB, ACTIONS.AND(ACTIONS.TURN, ACTIONS.PASS1));

            WALK.LinkTO(GODOWN).addPRECONDITION(PRECONDITIONS.GODOWN);
            WALK.LinkTO(WAIT).addPRECONDITION(PRECONDITIONS.BLOCK, ACTIONS.STOP);
            WALK.LinkTO(WAIT).addPRECONDITION(PRECONDITIONS.ABENEAR, ACTIONS.STOP);
            WALK.LinkTO(SNEAK).addPRECONDITION(PRECONDITIONS.ABESNEAK, ACTIONS.SNEAK1);
            WALK.LinkTO(WALK).addPRECONDITION(PRECONDITIONS.ABEBEHIND, ACTIONS.AND(ACTIONS.STOP, ACTIONS.AND(ACTIONS.TURN,ACTIONS.PASS1)));
            WALK.LinkTO(RUN).addPRECONDITION(PRECONDITIONS.ABERUN, ACTIONS.RUN1);          

            SNEAK.LinkTO(GODOWN).addPRECONDITION(PRECONDITIONS.GODOWN);
            SNEAK.LinkTO(WAIT).addPRECONDITION(PRECONDITIONS.BLOCK, ACTIONS.STOPSNEAK);
            SNEAK.LinkTO(WALK).addPRECONDITION(PRECONDITIONS.ABEwALK, ACTIONS.PASS1);
            SNEAK.LinkTO(RUN).addPRECONDITION(PRECONDITIONS.ABERUN, ACTIONS.RUN1);
            SNEAK.LinkTO(WAIT).addPRECONDITION(PRECONDITIONS.ABESAMEPOSITION, ACTIONS.STOPSNEAK);

            RUNSTART.LinkTO(RUN).addPRECONDITION(PRECONDITIONS.PORTALOPEN);
            RUNSTART.LinkTO(GODOWN).addPRECONDITION(PRECONDITIONS.GODOWN);
            RUNSTART.LinkTO(WAIT).addPRECONDITION(PRECONDITIONS.BLOCK, ACTIONS.AND(ACTIONS.KO, ACTIONS.STANDUP));
            RUNSTART.LinkTO(RUNSTOP).addPRECONDITION(PRECONDITIONS.ABEBEHIND);
            RUNSTART.LinkTO(RUN).addPRECONDITION(PRECONDITIONS.ABERUN);
            RUNSTART.LinkTO(RUNSTOP).addPRECONDITION(PRECONDITIONS.ABERUNSTOP);

            RUNSTOP.LinkTO(GODOWN).addPRECONDITION(PRECONDITIONS.GODOWN);
            RUNSTOP.LinkTO(WAIT).addPRECONDITION(PRECONDITIONS.BLOCK, ACTIONS.AND(ACTIONS.KO, ACTIONS.STANDUP));
            RUNSTOP.LinkTO(WAIT).addPRECONDITION(PRECONDITIONS.AND(PRECONDITIONS.ENDSTATE,PRECONDITIONS.ABEBEHIND),ACTIONS.TURN);
            RUNSTOP.LinkTO(WAIT).addPRECONDITION(PRECONDITIONS.ENDSTATE);

            RUN.LinkTO(RUNJUMPESCAPE).addPRECONDITION(PRECONDITIONS.PORTAL);
            RUN.LinkTO(GODOWN).addPRECONDITION(PRECONDITIONS.GODOWN);
            RUN.LinkTO(WAIT).addPRECONDITION(PRECONDITIONS.BLOCK,ACTIONS.AND(ACTIONS.KO,ACTIONS.STANDUP));
           //RUN.LinkTO(RUN).addPRECONDITION(PRECONDITIONS.ABEBEHIND, ACTIONS.AND(ACTIONS.RUNSTOP, ACTIONS.AND(ACTIONS.TURN, ACTIONS.RUN1)));
            RUN.LinkTO(RUNSTOP).addPRECONDITION(PRECONDITIONS.AND(PRECONDITIONS.ABEBEHIND,PRECONDITIONS.PORTALNOTOPEN));
            RUN.LinkTO(RUNSTOP).addPRECONDITION(PRECONDITIONS.ABERUNSTOP);
            RUN.LinkTO(WALK).addPRECONDITION(PRECONDITIONS.ABEwALK, ACTIONS.PASS1);
            RUN.LinkTO(SNEAK).addPRECONDITION(PRECONDITIONS.ABESNEAK, ACTIONS.SNEAK1);

            SHOOTED.LinkTO(DIED).addPRECONDITION(PRECONDITIONS.ENDSTATE);

            RUNJUMPESCAPE.LinkTO(DIED).addPRECONDITION(PRECONDITIONS.ENDSTATE);

            GODOWN.LinkTO(WAIT).addPRECONDITION(PRECONDITIONS.ENDSTATE, ACTIONS.PLANE);
            //GODOWN.LinkTO(HUNG).addPRECONDITION(PRECONDITIONS.AND(PRECONDITIONS.HOIST, PRECONDITIONS.ENDSTATE), ACTIONS.HUNGLONG);

            if(state == "CLEANDOWN")
                CURRENT_STATE = CLEANDOWN;
            if (state == "CLEANUP")
                CURRENT_STATE = CLEANUP;
            PREV_STATE = CURRENT_STATE;

        }

        //Esegue l'automa definito nel costruttore di MudokonAI
        //Viene chiamato nel metodo Update della classe Game1
        //[pp.48,49]
        public void ROUTINE()
        {
            if (CURRENT_STATE.name != "DIED")
            {
                #region ColorMask
                colTypes = game.CurrentLevel.collision(Mudokon);
                if (colTypes.Contains(COLLISIONS.GRAYAREA))
                {
                    Mudokon.color = Color.DarkSlateGray;
                }
                if (colTypes.Contains(COLLISIONS.BLACKAREA))
                {
                    Mudokon.color = Color.Black;
                }
                if (!colTypes.Contains(COLLISIONS.GRAYAREA) & !colTypes.Contains(COLLISIONS.BLACKAREA))
                {
                    Mudokon.color = Color.HotPink;
                } 
                #endregion
                //La transizione da questi stati può avvenire in qualsiasi momento
                if (CURRENT_STATE == CLEANUP || CURRENT_STATE == WAITfOLLOW || CURRENT_STATE == WAIT || CURRENT_STATE == WAITCOMMAND)
                {
                    #region HandleStateTransition
                    if (FINISHEDTransition)
                    {
                        precond = CURRENT_STATE.TryChangeState();
                        FINISHEDExecution = CURRENT_STATE.Execute();
                        if (FINISHEDExecution)
                        {
                            CURRENT_STATE.AnimsTODO[CURRENT_STATE.CURRENT_ANIM].Reset();
                            PREV_STATE = CURRENT_STATE;
                        }
                    }
                    if (precond != null)
                    {
                        if (precond.ACTION != null)
                            FINISHEDTransition = precond.ACTION.DO();

                        if (FINISHEDTransition)
                        {
                            CURRENT_STATE = precond.ARC.TO;
                            CURRENT_STATE.ENDSTATE = false;
                        }
                    } 
                    #endregion
                }
                else//Tutti gli altri stati esigono che tutte le animazione contenute siano state eseguite 
                {
                    //L'arco è stato percorso, esegue lo stato corrente
                    if (FINISHEDTransition)
                    {
                        if (FINISHEDExecution)
                            precond = CURRENT_STATE.TryChangeState();
                        if (precond != null)
                            FINISHEDExecution = true;
                        else
                            FINISHEDExecution = CURRENT_STATE.Execute();
                    }
                    //Tutte le animazione sono state seguite
                    if (FINISHEDExecution)
                    {
                        CURRENT_STATE.AnimsTODO[CURRENT_STATE.CURRENT_ANIM].Reset();
                        PREV_STATE = CURRENT_STATE;
                        if (FINISHEDTransition)
                            precond = CURRENT_STATE.TryChangeState();//Controllo precondizioni

                        if (precond != null)//Almeno una precondizione è stata verificata
                        {
                            if (precond.ACTION != null)//Se una precondizione ha associata un'azione
                                FINISHEDTransition = precond.ACTION.DO();//L'azione viene eseguita
                            if (FINISHEDTransition)
                            {
                                CURRENT_STATE = precond.ARC.TO;//L'automa transita nello stato successivo
                                CURRENT_STATE.ENDSTATE = false;
                            }
                        }
                    }
                }
            }
            else
            {
                //Gestione morte personaggio
                game.CurrentLevel.Mudokons.Remove(Mudokon);
                game.Abe.MUDOKONS.Remove(Mudokon);
            }
        }
    }

    //Definisce gli stati dell'automa
    //Ogni stato contiene una o più animazioni da eseguire
    //[p.45]
    public class STATEm
    {
        public bool ENDSTATE;
        public List<ARCm> ARCS;
        public List<Animation> AnimsTODO;
        public int CURRENT_ANIM = 0;
        public string name;
        public MudokonAI AI;
        public STATEm(string name, MudokonAI AI)
        {
            this.AI = AI;
            this.name = name;
            ENDSTATE = false;

            AnimsTODO = new List<Animation>();
            ARCS = new List<ARCm>();

            if (name == "WAIT")
            {
                AnimsTODO.Add(AI.Mudokon.WAIT);
            }
            if (name == "CLEANUP")
            {
                AnimsTODO.Add(AI.Mudokon.CLEANUP);
                AnimsTODO.Add(AI.Mudokon.CLEANUP);
                AnimsTODO.Add(AI.Mudokon.CLEANUP);
                AnimsTODO.Add(AI.Mudokon.RESTUP);
                AnimsTODO.Add(AI.Mudokon.RESTUP);
                AnimsTODO.Add(AI.Mudokon.RESTUP);
            }
            if (name == "CLEANDOWN")
            {
                AnimsTODO.Add(AI.Mudokon.CLEANDOWN);
                AnimsTODO.Add(AI.Mudokon.CLEANDOWN);
                AnimsTODO.Add(AI.Mudokon.CLEANDOWN);
                AnimsTODO.Add(AI.Mudokon.TURNdOWN);
                AnimsTODO.Add(AI.Mudokon.WAITcROUCH);
            }

            if (name == "WAITCOMMAND")
            {
                AnimsTODO.Add(AI.Mudokon.WAIT);
            }
            if (name == "JUMPCOMMAND")
            {
                AnimsTODO.Add(AI.Mudokon.JUMP_2);
                AnimsTODO.Add(AI.Mudokon.JUMP_3);
            }
            if (name == "RUNJUMPCOMMAND1")//PRENDE SPAZIO
            {
                AnimsTODO.Add(AI.Mudokon.TURN);
                AnimsTODO.Add(AI.Mudokon.PASS_1);
                AnimsTODO.Add(AI.Mudokon.PASS_2);
                //AnimsTODO.Add(AI.Mudokon.PASS_3);
                AnimsTODO.Add(AI.Mudokon.STOP);
                AnimsTODO.Add(AI.Mudokon.TURN);
            }
            if (name == "RUNJUMPCOMMAND2")//CORRE
            {
                AnimsTODO.Add(AI.Mudokon.RUN_1);
                AnimsTODO.Add(AI.Mudokon.RUN_2);
                AnimsTODO.Add(AI.Mudokon.RUN_3);
            }
            if (name == "RUNJUMPCOMMAND3")//SALTA
            {
                AnimsTODO.Add(AI.Mudokon.RUNJUMP2);
                AnimsTODO.Add(AI.Mudokon.RUNJUMP3);
                
            }
            if (name == "RUNJUMPCOMMAND4")//ATTERRA e FRENA
            {
                AnimsTODO.Add(AI.Mudokon.RUNJUMP4);
                AnimsTODO.Add(AI.Mudokon.RUNsTOP_1);
                AnimsTODO.Add(AI.Mudokon.RUNsTOP_2);
            }
            if (name == "GOTOABE")
            {
                AnimsTODO.Add(AI.Mudokon.PASS_2);
                AnimsTODO.Add(AI.Mudokon.PASS_3);
            }

            if (name == "WAITfOLLOW")
            {
                AnimsTODO.Add(AI.Mudokon.WAIT);
            }
            if (name == "WALK")
            {
                AnimsTODO.Add(AI.Mudokon.PASS_2);
                AnimsTODO.Add(AI.Mudokon.PASS_3);
            }

            if (name == "JUMP")
            {
                AnimsTODO.Add(AI.Mudokon.JUMP_1);
                AnimsTODO.Add(AI.Mudokon.JUMP_2);
                AnimsTODO.Add(AI.Mudokon.JUMP_3);
                AnimsTODO.Add(AI.Mudokon.JUMP_4);
            }
            if (name == "RUN")
            {
                AnimsTODO.Add(AI.Mudokon.RUN_4);
                AnimsTODO.Add(AI.Mudokon.RUN_5);
                AnimsTODO.Add(AI.Mudokon.RUN_2);
                AnimsTODO.Add(AI.Mudokon.RUN_3);
            }
            if (name == "RUNSTOP")
            {
                AnimsTODO.Add(AI.Mudokon.RUNsTOP_1);
                AnimsTODO.Add(AI.Mudokon.RUNsTOP_2);
            }
            if (name == "RUNSTART")
            {
                AnimsTODO.Add(AI.Mudokon.RUN_2);
                AnimsTODO.Add(AI.Mudokon.RUN_3);
            }
            if (name == "RUNJUMP")
            {
                AnimsTODO.Add(AI.Mudokon.RUNJUMP1);
                AnimsTODO.Add(AI.Mudokon.RUNJUMP2);
                AnimsTODO.Add(AI.Mudokon.RUNJUMP3);
                AnimsTODO.Add(AI.Mudokon.RUNJUMP4);
            }
            if (name == "RUNJUMPESCAPE")
            {
                AnimsTODO.Add(AI.Mudokon.RUNJUMP1);
                AnimsTODO.Add(AI.Mudokon.RUNJUMP2);
                AnimsTODO.Add(AI.Mudokon.RUNJUMP3);
            }
            if(name == "SNEAK")
            {
                AnimsTODO.Add(AI.Mudokon.SNEAK_2);
                AnimsTODO.Add(AI.Mudokon.SNEAK_3);
            }
            if (name == "GODOWN")
            {
                AnimsTODO.Add(AI.Mudokon.GODOWN);
            }
            if (name == "SHOOTED")
            {
                AnimsTODO.Add(AI.Mudokon.DIE);
            }
            if (name == "DIED")
            {
               
            }
            if (name != "DIED")
            {
                this.LinkTO(AI.DIED).addPRECONDITION(AI.PRECONDITIONS.BOTOLA,AI.ACTIONS.PLANE);
                this.LinkTO(AI.DIED).addPRECONDITION(AI.PRECONDITIONS.MATTATOIO, AI.ACTIONS.DIE);
            }      
        }

        //Collega due stati e restituisce l'arco associato
        public ARCm LinkTO(STATEm to)
        {
            ARCm arc = new ARCm(this.name + "to" + to.name, this, to);

            foreach (ARCm arcNew in ARCS)
            {
                if (arcNew.NAME == arc.NAME)
                    return arcNew;
            }
            this.ARCS.Add(arc);
            return arc;
        }

        //Esegue tutte le animazioni contenute nello stato corrente
        public bool Execute()
        {
            if (!AnimsTODO[CURRENT_ANIM].LastFrameDone)
            {
                AI.Mudokon.AnimateMudokon(AnimsTODO[CURRENT_ANIM]);
                return false;
            }
            else
            {
                if (CURRENT_ANIM == AnimsTODO.Count - 1)
                {
                    ENDSTATE = true;
                    CURRENT_ANIM = 0;
                }
                else
                {
                    CURRENT_ANIM++;
                }
                return true;
            }
        }

        //Controlla le precondizioni di tutti gli archi uscenti
        public PRECONDITIONm TryChangeState()
        {
            foreach (ARCm arc in AI.CURRENT_STATE.ARCS)
            {
                foreach (PRECONDITIONm precondition in arc.PRECONDITIONS)
                {
                    if (precondition.Verify())
                        return precondition;
                }
            }
            return null;
        }
    }

    //Definisce gli archi dell'automa
    //[p.47]
    public class ARCm
    {
        string name;
        STATEm from;
        STATEm to;

        List<PRECONDITIONm> preconditions;

        public ARCm(string name, STATEm from, STATEm to)
        {
            preconditions = new List<PRECONDITIONm>();
            this.name = name;
            this.from = from;
            this.to = to;
        }

        //Pone una precondizione sull'arco
        public void addPRECONDITION(PRECONDITIONm precondition)
        {
            PRECONDITIONm p = new PRECONDITIONm(from.AI, precondition.NAME);
            p.prec1 = precondition.prec1;
            p.prec2 = precondition.prec2;
            preconditions.Add(p);
            p.ARC = this;
        }

        //Pone una precondizione e un'azione sull'arco
        //Se la precondizione è verificata, viene eseguita l'azione
        public void addPRECONDITION(PRECONDITIONm precondition, ACTIONm action)
        {
            PRECONDITIONm p = new PRECONDITIONm(from.AI, precondition.NAME);
            p.prec1 = precondition.prec1;
            p.prec2 = precondition.prec2;
            preconditions.Add(p);
            p.addACTION(action);
            p.ARC = this;
        }
        #region Attribute
        public string NAME
        {
            get { return name; }
        }

        public List<PRECONDITIONm> PRECONDITIONS
        {
            get { return preconditions; }
        }

        public STATEm FROM
        {
            get { return from; }
        }

        public STATEm TO
        {
            get { return to; }
        } 
        #endregion
    }

    //Definisce le precondizioni che devono essere aggiunte ad un arco per transitare da uno stato all'altro.
    //[p.46]
    public class PRECONDITIONm
    {
        string name;
        MudokonAI AI;
        ACTIONm action;
        ARCm arc;
        public PRECONDITIONm prec1;
        public PRECONDITIONm prec2;
        public PRECONDITIONm(MudokonAI AI, string name)
        {
            this.name = name;
            this.AI = AI;
        }

        public void addACTION(ACTIONm action)
        {
            this.action = action;
        }

        //Controlla se la precondizione è soddisfatta
        public bool Verify()
        {
            if (name == "ABEWAIT")
            {
                if (AI.game.abeCONTROL.CURRENT_STATE.name == "WAIT")
                    return true;
            }

            if (name == "ABEwALK")
            {
                if (AI.game.abeCONTROL.CURRENT_STATE.name == "WALK")
                    return true;
            }
            if (name == "ABESNEAK")
            {
                if (AI.game.abeCONTROL.CURRENT_STATE.name == "SNEAK" || AI.game.abeCONTROL.CURRENT_STATE.CURRENTACTION == "SNEAK1")
                    return true;
            }
            if (name == "ABERUN")
            {
                if (AI.game.abeCONTROL.CURRENT_STATE.name == "RUN")
                    return true;
            }
            if (name == "ABERUNSTART")
            {
                if (AI.game.abeCONTROL.CURRENT_STATE.name == "RUNSTART")
                    return true;
            }
            if (name == "ABERUNSTOP")
            {
                if (AI.game.abeCONTROL.CURRENT_STATE.name == "RUNSTOP")
                    return true;
            }
            if (name == "ABESAMEPOSITION")
            {
                if (AI.game.Abe.boundingBox.X == AI.Mudokon.boundingBox.X)
                    return true;
            }
            if (name == "ENSsCREEN")
            {
                if ((AI.Mudokon.boundingBox.X <= 50 & AI.Mudokon.Direction == -1 & AI.Mudokon.CurrentLevel.NextLEFT == null) ||
                    (AI.Mudokon.boundingBox.Right >= 500 & AI.Mudokon.Direction == 1 & AI.Mudokon.CurrentLevel.NextRIGHT == null))
                {
                    return true;
                }
            }
            if (name == "ABENEAR")
            {
                if (AI.Mudokon.boundingBox.Intersects(AI.game.Abe.MUDOKONS[AI.Mudokon.POSITION - 1].boundingBox))
                    return true;
            }
            if (name == "ABENOTNEAR")
            {
                if (!AI.Mudokon.boundingBox.Intersects(AI.game.Abe.MUDOKONS[AI.Mudokon.POSITION-1].boundingBox))
                    return true;
            }
            if (name == "ABeHELLOB" & AI.game.CurrentLevel == AI.Mudokon.CurrentLevel)
            {
                if (AI.game.Abe.SAYS == "HELLO" & ((AI.game.Abe.boundingBox.X > AI.Mudokon.boundingBox.X & AI.Mudokon.Direction == -1 & AI.game.Abe.Direction == -1) || (AI.game.Abe.boundingBox.X < AI.Mudokon.boundingBox.X & AI.Mudokon.Direction == 1 & AI.game.Abe.Direction == 1)))
                {
                    AI.Mudokon.TURNdOWN.Reset();
                    AI.game.Abe.SAYS = "";
                    return true;
                }
            }
            if (name == "ABeHELLOA" & AI.game.CurrentLevel == AI.Mudokon.CurrentLevel)
            {
                if (AI.game.Abe.SAYS == "HELLO" & ((AI.game.Abe.boundingBox.X > AI.Mudokon.boundingBox.X & AI.Mudokon.Direction == 1 & AI.game.Abe.Direction == -1) || (AI.game.Abe.boundingBox.X < AI.Mudokon.boundingBox.X & AI.Mudokon.Direction == -1 & AI.game.Abe.Direction == 1)))
                {
                    AI.game.Abe.SAYS = "";
                    return true;
                }
            }
            if (name == "ABeHELLOLEFT" & AI.game.CurrentLevel == AI.Mudokon.CurrentLevel)
            {
                if (AI.game.Abe.SAYS == "HELLO" & (AI.game.Abe.boundingBox.X < AI.Mudokon.boundingBox.X))
                {
                    AI.game.Abe.SAYS = "";
                    return true;
                }
            }
            if (name == "ABeHELLORIGHT" & AI.game.CurrentLevel == AI.Mudokon.CurrentLevel)
            {
                if (AI.game.Abe.SAYS == "HELLO" & (AI.game.Abe.boundingBox.X > AI.Mudokon.boundingBox.X))
                {
                    AI.game.Abe.SAYS = "";
                    return true;
                }
            }
            if (name == "ABeFOLLOW" & AI.game.CurrentLevel == AI.Mudokon.CurrentLevel)
            {
                if (AI.game.Abe.SAYS == "FOLLOW")
                {
                    AI.game.Abe.SAYS = "";
                    if (!AI.game.Abe.MUDOKONS.Contains(AI.Mudokon))
                    {
                        AI.game.Abe.MUDOKONS.Add(AI.Mudokon);
                        AI.Mudokon.POSITION = AI.game.Abe.MUDOKONS.Count-1;
                    }
                    return true;
                }
            }
            if (name == "ABECOMMANDJUMP" & AI.game.CurrentLevel == AI.Mudokon.CurrentLevel)
            {
                if (AI.game.Abe.SAYS == "COMMANDJUMP")
                {
                    AI.game.Abe.SAYS = "";
                    return true;
                }
            }
            if (name == "ABECOMMANDRUNJUMP" & AI.game.CurrentLevel == AI.Mudokon.CurrentLevel)
            {
                if (AI.game.Abe.SAYS == "COMMANDRUNJUMP")
                {
                    AI.game.Abe.SAYS = "";
                    return true;
                }
            }
            if (name == "ABECALLB" & AI.game.CurrentLevel == AI.Mudokon.CurrentLevel)
            {
                if (AI.game.Abe.SAYS == "FOLLOW" & ((AI.game.Abe.boundingBox.X > AI.Mudokon.boundingBox.X & AI.Mudokon.Direction == -1 & AI.game.Abe.Direction == -1) || (AI.game.Abe.boundingBox.X < AI.Mudokon.boundingBox.X & AI.Mudokon.Direction == 1 & AI.game.Abe.Direction == 1)))
                {
                    if(AI.Mudokon == AI.game.Abe.MUDOKONS.Last()) 
                        AI.game.Abe.SAYS = "";
                    return true;
                }
            }
            if (name == "ABECALLA" & AI.game.CurrentLevel == AI.Mudokon.CurrentLevel)
            {
                if (AI.game.Abe.SAYS == "FOLLOW" & ((AI.game.Abe.boundingBox.X > AI.Mudokon.boundingBox.X & AI.Mudokon.Direction == 1 & AI.game.Abe.Direction == -1) || (AI.game.Abe.boundingBox.X < AI.Mudokon.boundingBox.X & AI.Mudokon.Direction == -1 & AI.game.Abe.Direction == 1)))
                {
                    if (AI.Mudokon == AI.game.Abe.MUDOKONS.Last())
                        AI.game.Abe.SAYS = "";
                    return true;
                }
            }
            if (name == "ABEWAITME")
            {
                if (AI.game.Abe.SAYS == "WAITME" & AI.game.CurrentLevel == AI.Mudokon.CurrentLevel)
                {
                    if (AI.Mudokon == AI.game.Abe.MUDOKONS.Last())
                        AI.game.Abe.SAYS = "";

                    if (AI.game.Abe.MUDOKONS.Contains(AI.Mudokon))
                    {
                        AI.game.Abe.MUDOKONS.RemoveAt(AI.Mudokon.POSITION);

                        for (int i = 0; i < AI.game.Abe.MUDOKONS.Count; i++)
                            AI.game.Abe.MUDOKONS[i].POSITION = i;
                    }
                    return true;
                }
            }
            if (name == "NOTABEOPPOSITE")
            {
                if (AI.Mudokon.Direction == AI.game.Abe.Direction)
                    return true;
            }
            if (name == "ABEBEHIND")
            {
                if ((AI.game.Abe.boundingBox.X < AI.Mudokon.boundingBox.X & AI.Mudokon.Direction == 1 || AI.game.Abe.boundingBox.X > AI.Mudokon.boundingBox.X & AI.Mudokon.Direction == -1) & AI.game.CurrentLevel == AI.Mudokon.CurrentLevel)
                    return true;
                if (AI.game.CurrentLevel == AI.Mudokon.CurrentLevel.NextLEFT & AI.Mudokon.Direction == 1 || AI.game.CurrentLevel == AI.Mudokon.CurrentLevel.NextRIGHT & AI.Mudokon.Direction == -1)
                    return true;
            }
            if (name == "ABEAHEAD")
            {
                int dist = AI.Mudokon.boundingBox.X - AI.game.Abe.boundingBox.X;
                if (!AI.Mudokon.boundingBox.Intersects(AI.game.Abe.boundingBox) & ((dist < 0 & AI.Mudokon.Direction == 1) || (dist > 0 & AI.Mudokon.Direction == -1)))
                    return true;
            }

            if (name == "ENDSTATE")
            {
                if (AI.CURRENT_STATE.ENDSTATE)
                {
                    if (AI.CURRENT_STATE == AI.RUNJUMP)
                    {
                        AI.game.Abe.MUDOKONS.Remove(AI.Mudokon);
                        AI.game.CurrentLevel.Mudokons.Remove(AI.Mudokon);
                    }
                    return true;
                }
            }
            if (name == "ABEDOWN")
            {
                if (AI.game.Abe.CurrentLevel == AI.Mudokon.CurrentLevel)
                {
                    if (AI.game.Abe.MUDOKONS[AI.Mudokon.POSITION - 1].boundingBox.Y > AI.Mudokon.boundingBox.Bottom)
                        return true;
                }
                else
                {
                    if (AI.game.Abe.MUDOKONS[AI.Mudokon.POSITION - 1].boundingBox.Bottom < AI.Mudokon.boundingBox.Y)
                        return true;
                }
            }
            if (name == "ABEUP")
            {
                if (AI.game.Abe.CurrentLevel == AI.Mudokon.CurrentLevel)
                {
                    if (AI.game.Abe.MUDOKONS[AI.Mudokon.POSITION - 1].boundingBox.Bottom < AI.Mudokon.boundingBox.Y)
                        return true;
                }
                else
                {
                    if (AI.game.Abe.MUDOKONS[AI.Mudokon.POSITION - 1].boundingBox.Y > AI.Mudokon.boundingBox.Bottom)
                        return true;
                }
            }
            if (name == "ABESAMEFLOOR")
            {
                if (AI.game.Abe.CurrentLevel == AI.Mudokon.CurrentLevel)
                {
                    if (AI.game.Abe.MUDOKONS[AI.Mudokon.POSITION - 1].boundingBox.Y == AI.Mudokon.boundingBox.Y)
                        return true;
                }
            }
            if (name == "HOIST")
            {
                List<string> colTypes = new List<string>();
                colTypes = AI.Mudokon.CurrentLevel.collision(AI.Mudokon);
                if (colTypes.Contains(COLLISIONS.HOISTDESTRO) & AI.Mudokon.Direction == 1 || colTypes.Contains(COLLISIONS.HOISTSINISTRO) & AI.Mudokon.Direction == -1)
                    return true;
            }
            if (name == "NOTHOIST")
            {
                List<string> colTypes = new List<string>();
                colTypes = AI.Mudokon.CurrentLevel.collision(AI.Mudokon);
                if (!colTypes.Contains(COLLISIONS.HOISTDESTRO) & !colTypes.Contains(COLLISIONS.HOISTSINISTRO))
                    return true;
            }
            if (name == "HOISTTURN")
            {
                List<string> colTypes = new List<string>();
                colTypes = AI.Mudokon.CurrentLevel.collision(AI.Mudokon);
                if (colTypes.Contains(COLLISIONS.HOISTDESTRO) & AI.Mudokon.Direction == -1 || colTypes.Contains(COLLISIONS.HOISTSINISTRO) & AI.Mudokon.Direction == 1)
                    return true;
            }
            if (name == "APPIGLIO")
            {
                List<string> colTypes = new List<string>();
                colTypes = AI.Mudokon.CurrentLevel.collision(AI.Mudokon);
                if (colTypes.Contains(COLLISIONS.APPIGLIODESTRO) & AI.Mudokon.Direction == 1 || colTypes.Contains(COLLISIONS.APPIGLIOSINISTRO) & AI.Mudokon.Direction == -1)
                    return true;
            }
            if (name == "APPIGLIOTURN")
            {
                List<string> colTypes = new List<string>();
                colTypes = AI.Mudokon.CurrentLevel.collision(AI.Mudokon);
                if (colTypes.Contains(COLLISIONS.APPIGLIODESTRO) & AI.Mudokon.Direction == -1 || colTypes.Contains(COLLISIONS.APPIGLIOSINISTRO) & AI.Mudokon.Direction == 1)
                    return true;
            }

            if (name == "BLOCK")
            {
                List<string> colTypes = new List<string>();

                colTypes = AI.Mudokon.CurrentLevel.collision(AI.Mudokon);
                if (colTypes.Contains(COLLISIONS.BLOCCO))
                    return true;
            }

            if (name == "GODOWN")
            {
                List<string> colTypes = new List<string>();

                colTypes = AI.Mudokon.CurrentLevel.collision(AI.Mudokon);
                if (colTypes.Contains(COLLISIONS.GODOWN))
                    return true;
            }
            if (name == "MATTATOIO")
            {
                List<string> colTypes = new List<string>();

                colTypes = AI.game.CurrentLevel.collision(AI.Mudokon);
                if (colTypes.Contains(COLLISIONS.MATTATOIO))
                    return true;
            }

            if (name == "BOTOLA")
            {
                List<string> colTypes = new List<string>();

                colTypes = AI.game.CurrentLevel.collision(AI.Mudokon);
                if (colTypes.Contains(COLLISIONS.BOTOLA))
                    return true;
            }
            if (name == "DIED")
            {
                if (AI.Mudokon.DEAD)
                {
                    AI.Mudokon.boundingBox = new Microsoft.Xna.Framework.Rectangle(AI.Mudokon.boundingBox.X, AI.Mudokon.boundingBox.Y, 0, 0);
                    return true;
                }
            }
            if (name == "PORTAL")
            {
                List<string> colTypes = new List<string>();

                colTypes = AI.game.CurrentLevel.collision(AI.Mudokon);
                if (colTypes.Contains(COLLISIONS.PORTAL))
                    return true;
            }
            if (name == "PORTALOPEN")
            {
                if (AI.game.IsPORTALOPEN)
                    return true;
            }
            if (name == "PORTALOPENAHEAD")
            {
                if (AI.game.IsPORTALOPEN)
                {
                    ObjectA PORTAL = null;
                    foreach (ObjectA OBJ in AI.game.CurrentLevel.ObjectsA)
                    {
                        if (OBJ.NAME == "PORTAL")
                            PORTAL = OBJ;
                    }
                    if (PORTAL != null)
                    {
                        if (PORTAL.COLLISION.REC.X > AI.Mudokon.boundingBox.X && AI.Mudokon.Direction == 1 || PORTAL.COLLISION.REC.Right < AI.Mudokon.boundingBox.X && AI.Mudokon.Direction == -1)
                            return true;
                    }                
                }                
            }
            if (name == "PORTALOPENBEHIND")
            {
                if (AI.game.IsPORTALOPEN)
                {
                    ObjectA PORTAL = null;
                    foreach (ObjectA OBJ in AI.game.CurrentLevel.ObjectsA)
                    {
                        if (OBJ.NAME == "PORTAL")
                            PORTAL = OBJ;
                    }
                    if (PORTAL != null)
                    {
                        if (PORTAL.COLLISION.REC.X > AI.Mudokon.boundingBox.X && AI.Mudokon.Direction == -1 || PORTAL.COLLISION.REC.Right < AI.Mudokon.boundingBox.X && AI.Mudokon.Direction == 1)
                            return true;
                    }
                }
            }
            if (name == "PORTALRIGHT")
            {
                if (AI.game.IsPORTALOPEN)
                {
                    ObjectA PORTAL = null;
                    foreach (ObjectA OBJ in AI.game.CurrentLevel.ObjectsA)
                    {
                        if (OBJ.NAME == "PORTAL")
                            PORTAL = OBJ;
                    }
                    if (PORTAL != null)
                    {
                        if (PORTAL.COLLISION.REC.X > AI.Mudokon.boundingBox.X)
                            return true;
                    }
                }
            }
            if (name == "PORTALLEFT")
            {
                if (AI.game.IsPORTALOPEN)
                {
                    ObjectA PORTAL = null;
                    foreach (ObjectA OBJ in AI.game.CurrentLevel.ObjectsA)
                    {
                        if (OBJ.NAME == "PORTAL")
                            PORTAL = OBJ;
                    }
                    if (PORTAL != null)
                    {
                        if (PORTAL.COLLISION.REC.X < AI.Mudokon.boundingBox.X)
                            return true;
                    }
                }
            }
            if (name == "AND")
            {
                return this.prec1.Verify() & this.prec2.Verify();
            }

            return false;
        }

        #region Attribute
        public ACTIONm ACTION
        {
            get { return action; }
        }
        public ARCm ARC
        {
            get { return arc; }
            set { arc = value; }
        }
        public string NAME
        {
            get { return name; }
        } 
        #endregion
    }

    //Contiene i riferimenti a tutte le istanze della classe PRECONDITIONm 
    public class PRECONDITIONSm
    {
        MudokonAI AI;
        public PRECONDITIONm ABEWAIT;
        public PRECONDITIONm ABEwALK;
        public PRECONDITIONm ABERUN;
        public PRECONDITIONm ABERUNSTART;
        public PRECONDITIONm ABERUNSTOP;
        public PRECONDITIONm ENSsCREEN;
        public PRECONDITIONm ABENEAR;
        public PRECONDITIONm ABENOTNEAR;
        public PRECONDITIONm ABESNEAK;
        public PRECONDITIONm ABeHELLOB;
        public PRECONDITIONm ABeHELLOA;
        public PRECONDITIONm ABeHELLOLEFT;
        public PRECONDITIONm ABeHELLORIGHT;
        public PRECONDITIONm ABeFOLLOW;
        public PRECONDITIONm ABEBEHIND;
        public PRECONDITIONm ABEAHEAD;
        public PRECONDITIONm ABEWAITME;
        public PRECONDITIONm ABECALLA;
        public PRECONDITIONm ABECALLB;
        public PRECONDITIONm ABECOMMANDJUMP;
        public PRECONDITIONm ABECOMMANDRUNJUMP;
        public PRECONDITIONm ENDSTATE;
        public PRECONDITIONm NOTABEOPPOSITE;
        public PRECONDITIONm ABESAMEPOSITION;
        public PRECONDITIONm BLOCK;
        public PRECONDITIONm HOIST;
        public PRECONDITIONm NOTHOIST;
        public PRECONDITIONm HOISTTURN;
        public PRECONDITIONm APPIGLIO;
        public PRECONDITIONm APPIGLIOTURN;
        public PRECONDITIONm DIED;
        public PRECONDITIONm MATTATOIO;
        public PRECONDITIONm BOTOLA;
        public PRECONDITIONm GODOWN;
        public PRECONDITIONm PORTALNOTOPEN;
        public PRECONDITIONm PORTALOPEN;
        public PRECONDITIONm PORTALOPENAHEAD;
        public PRECONDITIONm PORTALOPENBEHIND;
        public PRECONDITIONm PORTAL;
        public PRECONDITIONm PORTALLEFT;
        public PRECONDITIONm PORTALRIGHT;
        public PRECONDITIONm ABEDOWN;
        public PRECONDITIONm ABEUP;
        public PRECONDITIONm ABESAMEFLOOR;

        public PRECONDITIONSm(MudokonAI AI)
        {
            this.AI = AI;
            ABEwALK = new PRECONDITIONm(AI, "ABEwALK");
            ABERUN = new PRECONDITIONm(AI, "ABERUN");
            ABERUNSTART = new PRECONDITIONm(AI, "ABERUNSTART");
            ABERUNSTOP = new PRECONDITIONm(AI, "ABERUNSTOP");
            ENSsCREEN = new PRECONDITIONm(AI, "ENSsCREEN");
            ABENEAR = new PRECONDITIONm(AI, "ABENEAR");
            ABENOTNEAR = new PRECONDITIONm(AI, "ABENOTNEAR");
            ABESNEAK = new PRECONDITIONm(AI, "ABESNEAK");
            ABeHELLOB = new PRECONDITIONm(AI, "ABeHELLOB");
            ABeHELLOA = new PRECONDITIONm(AI, "ABeHELLOA");
            ABeHELLOLEFT = new PRECONDITIONm(AI, "ABeHELLOLEFT");
            ABeHELLORIGHT = new PRECONDITIONm(AI, "ABeHELLORIGHT");
            ABeFOLLOW = new PRECONDITIONm(AI, "ABeFOLLOW");
            ABEBEHIND = new PRECONDITIONm(AI, "ABEBEHIND");
            ABEAHEAD = new PRECONDITIONm(AI, "ABEAHEAD");
            ABEWAIT = new PRECONDITIONm(AI, "ABEWAIT");
            ABEWAITME = new PRECONDITIONm(AI, "ABEWAITME");
            ABECALLA = new PRECONDITIONm(AI, "ABECALLA");
            ABECALLB = new PRECONDITIONm(AI, "ABECALLB");
            ABECOMMANDJUMP = new PRECONDITIONm(AI, "ABECOMMANDJUMP");
            ABECOMMANDRUNJUMP = new PRECONDITIONm(AI, "ABECOMMANDRUNJUMP");
            ENDSTATE = new PRECONDITIONm(AI, "ENDSTATE");
            NOTABEOPPOSITE = new PRECONDITIONm(AI, "NOTABEOPPOSITE");
            ABESAMEPOSITION = new PRECONDITIONm(AI, "ABESAMEPOSITION");
            BLOCK = new PRECONDITIONm(AI, "BLOCK");
            HOIST = new PRECONDITIONm(AI, "HOIST");
            NOTHOIST = new PRECONDITIONm(AI, "NOTHOIST");
            HOISTTURN = new PRECONDITIONm(AI, "HOISTTURN");
            APPIGLIO = new PRECONDITIONm(AI, "APPIGLIO");
            APPIGLIOTURN = new PRECONDITIONm(AI, "APPIGLIOTURN");
            DIED = new PRECONDITIONm(AI, "DIED");
            MATTATOIO = new PRECONDITIONm(AI, "MATTATOIO");
            BOTOLA = new PRECONDITIONm(AI, "BOTOLA");
            GODOWN = new PRECONDITIONm(AI, "GODOWN");
            PORTALOPENAHEAD = new PRECONDITIONm(AI, "PORTALOPENAHEAD");
            PORTALOPENBEHIND = new PRECONDITIONm(AI, "PORTALOPENBEHIND");
            PORTALLEFT = new PRECONDITIONm(AI, "PORTALLEFT");
            PORTALRIGHT = new PRECONDITIONm(AI, "PORTALRIGHT");
            PORTALOPEN = new PRECONDITIONm(AI, "PORTALOPEN");
            PORTALNOTOPEN = new PRECONDITIONm(AI, "PORTALNOTOPEN");
            PORTAL = new PRECONDITIONm(AI, "PORTAL");
            ABEDOWN = new PRECONDITIONm(AI, "ABEDOWN");
            ABEUP = new PRECONDITIONm(AI, "ABEUP");
            ABESAMEFLOOR = new PRECONDITIONm(AI, "ABESAMEFLOOR");
        }

        public PRECONDITIONm AND(PRECONDITIONm prec1, PRECONDITIONm prec2)
        {
            PRECONDITIONm precResult = new PRECONDITIONm(AI, "AND");
            precResult.prec1 = prec1;
            precResult.prec2 = prec2;

            return precResult;

        }
    }

    //Definisce le azioni che possono essere associate ad una precondizione
    //Ogni azione può contenere una o più precondizioni.
    //[p.45]
    public class ACTIONm
    {
        MudokonAI AI;
        string name;
        public List<Animation> AnimsTODO;
        int CURRENT_ANIM = 0;
        public ACTIONm action1;
        public ACTIONm action2;

        public ACTIONm(MudokonAI AI, string name)
        {
            AnimsTODO = new List<Animation>();
            this.AI = AI;
            this.name = name;

            if (name == "STOP_TURN_PASS1")
            {
                AnimsTODO.Add(AI.Mudokon.STOP);
                AnimsTODO.Add(AI.Mudokon.TURN);
                AnimsTODO.Add(AI.Mudokon.PASS_1);
            }
            if (name == "TURN")
            {
                AnimsTODO.Add(AI.Mudokon.TURN);
            }
            if (name == "TURNDOWN")
            {
                AnimsTODO.Add(AI.Mudokon.TURNdOWN);
            }
            if (name == "STANDUP")
            {
                AnimsTODO.Add(AI.Mudokon.STANDUP);
            }
            if (name == "CLEANUPSTOPLEFT")
            {
                AnimsTODO.Add(AI.Mudokon.CLEANUPSTOPLEFT);
            }
            if (name == "CLEANUPSTOPRIGHT")
            {
                AnimsTODO.Add(AI.Mudokon.CLEANUPSTOPRIGHT);
            }
            if (name == "CROUCH")
            {
                AnimsTODO.Add(AI.Mudokon.CROUCH);
            }
            if (name == "STOP")
            {
                AnimsTODO.Add(AI.Mudokon.STOP);
            }
            if (name == "STOPRUNJUMP")
            {
                AnimsTODO.Add(AI.Mudokon.RUNJUMP4);
            }
            if (name == "PASS1")
            {
                AnimsTODO.Add(AI.Mudokon.PASS_1);
            }
            if (name == "RUNJUMP1")
            {
                AnimsTODO.Add(AI.Mudokon.RUNJUMP1);
            }
            if (name == "STOPSNEAK_TURN_SNEAK1")
            {
                AnimsTODO.Add(AI.Mudokon.SNEAKsTOP);
                AnimsTODO.Add(AI.Mudokon.TURN);
                AnimsTODO.Add(AI.Mudokon.SNEAK_1);
            }
            if (name == "STOPSNEAK")
            {
                AnimsTODO.Add(AI.Mudokon.SNEAKsTOP);
            }

            if (name == "SNEAK1")
            {
                AnimsTODO.Add(AI.Mudokon.SNEAK_1);
            }
            if (name == "RUN1")
            {
                AnimsTODO.Add(AI.Mudokon.RUN_1);
                AnimsTODO.Add(AI.Mudokon.RUN_2);
            }
            if (name == "RUNSTOP")
            {
                AnimsTODO.Add(AI.Mudokon.RUNsTOP_1);
                AnimsTODO.Add(AI.Mudokon.RUNsTOP_2);
            }
            if (name == "TURN_HELLO")
            {
                AnimsTODO.Add(AI.Mudokon.TURN);
                AnimsTODO.Add(AI.Mudokon.HELLO);
            }
            if (name == "HELLO")
            {
                AnimsTODO.Add(AI.Mudokon.HELLO);
            }
            if (name == "OK")
            {
                AnimsTODO.Add(AI.Mudokon.OK);
            }
            if (name == "PLANE")
            {
                AnimsTODO.Add(AI.Mudokon.PLANE);
            }
            if (name == "HOISTUP")
            {
                AnimsTODO.Add(AI.Mudokon.HOISTUP);
            }
            if (name == "HOISTDOWN")
            {
                AnimsTODO.Add(AI.Mudokon.HOISTDOWN);
            }
            if (name == "JUMPUP")
            {
                AnimsTODO.Add(AI.Mudokon.JUMPUP);
            }
            if (name == "JUMPDOWN")
            {
                AnimsTODO.Add(AI.Mudokon.JUMPDOWN);
            }
            if (name == "JUMP1")
            {
                AnimsTODO.Add(AI.Mudokon.JUMP_1);
            }
            if (name == "JUMP4")
            {
                AnimsTODO.Add(AI.Mudokon.JUMP_4);
            }
            if (name == "KO")
            {
                AnimsTODO.Add(AI.Mudokon.KO);
            }
            if (name == "GETUP")
            {
                AnimsTODO.Add(AI.Mudokon.GETUP);
            }
            if (name == "DIE")
            {
                AnimsTODO.Add(AI.Mudokon.DIE);
            }
        }

        //Metodo chiamato per eseguire tutte le animazioni contenute in un'azione
        public bool DO()
        {
            if (!AnimsTODO[CURRENT_ANIM].LastFrameDone)
            {
                AI.Mudokon.AnimateMudokon(AnimsTODO[CURRENT_ANIM]);
                return false;
            }
            else
            {
                if (CURRENT_ANIM == AnimsTODO.Count - 1)
                {
                    CURRENT_ANIM = 0;
                    AI.CURRENT_STATE.CURRENT_ANIM = 0;
                    return true;
                }
                else
                {
                    CURRENT_ANIM++;
                }
                return false;
            }
        }
        #region Attribute
        public string NAME
        {
            get { return name; }
        } 
        #endregion
    }

    //Contiene i riferimenti a tutte le istanze della classe ACTIONm
    public class ACTIONSm
    {
        MudokonAI AI;
        public ACTIONm STOP_TURN_PASS1;
        public ACTIONm STOP;
        public ACTIONm PASS1;
        public ACTIONm RUN1;
        public ACTIONm RUNJUMP1;
        public ACTIONm RUNSTOP;
        public ACTIONm STOPSNEAK;
        public ACTIONm SNEAK1;
        public ACTIONm STOPSNEAK_TURN_SNEAK1;
        public ACTIONm HELLO;
        public ACTIONm TURN_HELLO;
        public ACTIONm OK;
        public ACTIONm TURN;
        public ACTIONm TURNDOWN;
        public ACTIONm STANDUP;
        public ACTIONm CLEANUPSTOPLEFT;
        public ACTIONm CLEANUPSTOPRIGHT;
        public ACTIONm CROUCH;
        public ACTIONm DIE;
        public ACTIONm PLANE;
        public ACTIONm STOPRUNJUMP;
        public ACTIONm HOISTUP;
        public ACTIONm HOISTDOWN;
        public ACTIONm JUMPUP;
        public ACTIONm JUMPDOWN;
        public ACTIONm JUMP1;
        public ACTIONm JUMP4;
        public ACTIONm KO;
        public ACTIONm GETUP;

        public ACTIONSm(MudokonAI AI)
        {
            this.AI = AI;
            STOP_TURN_PASS1 = new ACTIONm(AI, "STOP_TURN_PASS1");
            STOP = new ACTIONm(AI, "STOP");
            PASS1 = new ACTIONm(AI, "PASS1");
            STOPSNEAK = new ACTIONm(AI, "STOPSNEAK");
            SNEAK1 = new ACTIONm(AI, "SNEAK1");
            STOPSNEAK_TURN_SNEAK1 = new ACTIONm(AI, "STOPSNEAK_TURN_SNEAK1");
            HELLO = new ACTIONm(AI, "HELLO");
            TURN_HELLO = new ACTIONm(AI, "TURN_HELLO");
            OK = new ACTIONm(AI, "OK");
            TURN = new ACTIONm(AI, "TURN");
            TURNDOWN = new ACTIONm(AI, "TURNDOWN");
            RUN1 = new ACTIONm(AI, "RUN1");
            RUNJUMP1 = new ACTIONm(AI, "RUNJUMP1");
            RUNSTOP = new ACTIONm(AI, "RUNSTOP");
            STANDUP = new ACTIONm(AI, "STANDUP");
            CLEANUPSTOPLEFT = new ACTIONm(AI, "CLEANUPSTOPLEFT");
            CLEANUPSTOPRIGHT = new ACTIONm(AI, "CLEANUPSTOPRIGHT");
            CROUCH = new ACTIONm(AI, "CROUCH");
            DIE = new ACTIONm(AI, "DIE");
            PLANE = new ACTIONm(AI, "PLANE");
            STOPRUNJUMP = new ACTIONm(AI, "STOPRUNJUMP");
            HOISTUP = new ACTIONm(AI, "HOISTUP");
            HOISTDOWN = new ACTIONm(AI, "HOISTDOWN");
            JUMPUP = new ACTIONm(AI, "JUMPUP");
            JUMPDOWN = new ACTIONm(AI, "JUMPDOWN");
            JUMP1 = new ACTIONm(AI, "JUMP1");
            JUMP4 = new ACTIONm(AI, "JUMP4");
            KO = new ACTIONm(AI, "KO");
            GETUP = new ACTIONm(AI, "GETUP");         
        }

        public ACTIONm AND(ACTIONm action1, ACTIONm action2)
        {
            ACTIONm action = new ACTIONm(AI, "AND");

            foreach (Animation anim in action1.AnimsTODO)
                action.AnimsTODO.Add(anim);
            foreach (Animation anim in action2.AnimsTODO)
                action.AnimsTODO.Add(anim);


            return action;
        }
    }
}

